
#include "detector.h"

#include <iostream>

Detector::Detector(int argc, char** argv)
	:img_count_all(0)
	,obj_count(0)
	,img_source(NULL)
{

	if(argc>1 && strlen(argv[1]) > 0)
	{
		cascade_path = argv[1];
		if(argc > 2 && strncmp(argv[2], "--camera",	strlen("--camera") ) == 0 ) 
		{
			work_mode = 1;
			img_source = new CamGetter();

		}else if(argc > 3 && strncmp(argv[2], "--directory", strlen("--directory") ) == 0 )
		{
			work_mode = 2;
			string directory_path = argv[3];
			img_source = new DirGetter(directory_path);
			create_directory("Detected");
		}
		else
		{
			throw my_error() << my_info("Unexpected work mode");
		}
	}
	else
	{
		throw my_error() << my_info("Incorrect classifier name");
	}

	if(!faces.load(FileParse("facecascade")))
	{
		isCascadeLoad = false;
		throw my_error() << my_info("Cascade did not load");
	}
	if(!eyes.load(FileParse("eyecascade")))
	{
		isCascadeLoad = false;
		throw my_error() << my_info("Cascade did not load");
	}
	if(!noses.load(FileParse("nosecascade")))
	{
		isCascadeLoad = false;
		throw my_error() << my_info("Cascade did not load");
	}
	if(!mouths.load(FileParse("mouthcascade")))
	{
		isCascadeLoad = false;
		throw my_error() << my_info("Cascade did not load");
	}
	faces_type = faces.featureType;
	cout << "Cascade type is" << (faces_type == 0) ? "Haar" : "Lbp";
	isCascadeLoad = true;
	
}
Detector::~Detector()
{
	delete img_source;
}

bool Detector::Detect(const Mat & img, double scale, vector<Rect> & result)
{
	if(faces.empty())
	{
		throw my_error() << my_info("Faces did not load");
	}
	result.clear();
	faces.detectMultiScale(img, result, scale, 3, 0, Size(60,60));
	return true;
}
// function return true if cascades loaded and on pictures detected 2 eyes 1 mouth and 1 nose
bool Detector::DetectFeatures(const Mat & img, double scale, FEATURES (& result)[3])
{
	if(eyes.empty())
	{
		throw my_error() << my_info("Faces did not load");
	}
	if(mouths.empty())
	{
		throw my_error() << my_info("Mouths did not load");
	}
	if(noses.empty())
	{
		throw my_error() << my_info("Noses did not load");
	}
	for(size_t i = 0; i < 3; ++i)
	{ 
		result[i].clear();
	}
	eyes.detectMultiScale(img, result[EYES_DETECTED], scale, 3, 0, Size(10,10));
	mouths.detectMultiScale(img, result[MOUTHS_DETECTED], scale, 3, 0, Size(10,10));
	noses.detectMultiScale(img, result[NOSES_DETECTED], scale, 3, 0, Size(10,10));
	if(result[EYES_DETECTED].size() == 2 && result[MOUTHS_DETECTED].size() == 1 && result[NOSES_DETECTED].size() == 1)
		return true;
	return false;
}

bool Detector::SaveImage(const string & img_path, Mat const & img)
{
	vector<int> p;
	p.push_back(CV_IMWRITE_JPEG_QUALITY);
	p.push_back(75);
	p.push_back(0);
	return imwrite(img_path, img, p);
}

string Detector::FileParse(string const & param)
{
	string s;
	if(cascades_file == "")
	{
		ifstream in(cascade_path);
		
		while(in >> s)
		{
			cascades_file += s + " ";
		}
		in.close();
	}
	 
	string res;
	int i = cascades_file.find(param + " = ");
	if(i != -1 && i != cascades_file.size())
	{
		stringstream result;
		result << cascades_file.substr(i + param.size() + 3, cascades_file.size());
		result >> res;
	}
	return res;
}

int Detector::Run()
{
	vector<Rect> results;
	if(work_mode == 1)
	{
		namedWindow("main_window",1);
	}
	for(Mat now_img; img_source -> NextImg(now_img); ++img_count_all ) 
	{
		double const scale = 1.1;
		Detect(now_img, scale, results);
		Point pt1, pt2;
		for(size_t i = 0; i != results.size(); ++i)
		{
			Rect r = results[i];
			pt1.x = r.x;
			pt2.x = (r.x+r.width);
			pt1.y = r.y;
			pt2.y = (r.y+r.height);

			if(work_mode == 1)
			{
				rectangle( now_img, pt1, pt2, Scalar(255,0,0), 3, 8, 0 );
				imshow( "main_window", now_img );
				if( waitKey( 10 ) >= 0 )
					break;
			}
			if(work_mode == 2)
			{

				Mat obj (now_img, Rect(pt1, pt2) );
				FEATURES features[3];
				++obj_count;
				stringstream newimg;
				newimg << "Detected/face" << obj_count << ".jpg";
				///*/if(
				DetectFeatures(obj, scale, features); 
				//== true)
				{
					
					for(size_t i = 0; i < 3; ++i)
					{
						for(size_t j = 0; j < features[i].size(); ++j)
						{
							Rect f = features[i][j];
							pt1.x = f.x;
							pt2.x = (f.x+f.width);
							pt1.y = f.y;
							pt2.y = (f.y+f.height);
							if(j == MOUTHS_DETECTED)//blue
								rectangle(obj, pt1, pt2, Scalar(255,0,0), 3, 8, 0);
							if(j == EYES_DETECTED)//green
								rectangle(obj, pt1, pt2, Scalar(0,255,0), 3, 8, 0);
							if(j == NOSES_DETECTED)//nose
								rectangle(obj, pt1, pt2, Scalar(0,0,255), 3, 8, 0);
						}
					}
					//*/
					SaveImage(newimg.str(), obj);
				}


			}
		}
	}
	if(work_mode == 1)
	{
		destroyWindow("main_window");
	}
	cout << "Images processed: " << img_count_all <<endl;
	cout << "Objects recognized: " << obj_count <<endl;
	return 0;
}

